package task

import (
	"io"
	"os"

	"file_explorer/common/log"
	"file_explorer/common/util"
	"file_explorer/model"
)

type fileCopier struct {
	BasicTask

	srcFileInfo *model.FileInfo
	dstPath     string

	onCopiedUnit func(curSize int64)
}

func (task *fileCopier) start() {
	task.BasicTask.start()
	defer task.BasicTask.finish()

	err := task.copyRegularFile()
	if err != nil {
		task.fail(err)
	}
}

func (task *fileCopier) copyRegularFile() error {
	// 解决文件名冲突
	dstPath, err := util.ResolveFilenameConflict(task.dstPath)
	if err != nil {
		return err
	}

	srcPath := task.srcFileInfo.GetPath()
	src, err := os.OpenFile(srcPath, os.O_RDONLY, 0644)
	if err != nil {
		log.Error("open src file failed, path=%v, err=%v", srcPath, err)
		return err
	}

	dst, err := os.Create(dstPath)
	if err != nil {
		log.Error("create dst file failed, path=%v, err=%v", dstPath, err)
		return err
	}

	log.Debug("copy %v -> %v start", srcPath, dstPath)

	curSize := int64(0)
	buf := make([]byte, 1024*4)
	for {
		if !task.isRunning() {
			break
		}

		length, err := src.Read(buf)
		if err != nil && err != io.EOF {
			log.Error("read file failed, path=%v, err=%v", srcPath, err)
			return err
		}
		if length == 0 {
			break
		}
		_, err = dst.Write(buf[:length])
		if err != nil {
			log.Error("write file failed, path=%v, err=%v", srcPath, err)
			return err
		}

		curSize += int64(length)
		if task.onCopiedUnit != nil {
			task.onCopiedUnit(curSize)
		}
	}

	if task.err != nil {
		log.Debug("copy file cancelled, src=%v, dst=%v", srcPath, dstPath)
		err := os.RemoveAll(dstPath)
		return err
	}

	log.Debug("copy %v -> %v end", srcPath, dstPath)

	return nil
}

func newFileCopier(srcFileInfo *model.FileInfo, dstPath string, onCopiedUnit func(int64)) *fileCopier {
	if srcFileInfo.IsDir() {
		panic(interface{}("create file copier with a directory"))
	}
	return &fileCopier{
		BasicTask:    newBasicTask("file copier"),
		srcFileInfo:  srcFileInfo,
		dstPath:      dstPath,
		onCopiedUnit: onCopiedUnit,
	}
}
